package com.demo.lock;


import java.util.HashMap;

/**
 * Created by hunter
 *
 * @Description 一个简单的乐观锁Demo, 类如更新数据时, 带着从数据库的版本号去更新, 更新时会比对版本号, 如若不一致则更新不成功
 *              具体应用像 CAS
 */
public class OptimisticLock {

    // 当做一个DB
    private static final HashMap<Integer, Data> DB_MAP = new HashMap<>();

    // 填充DB数据
    static {
        DB_MAP.put(1, new Data(1, 1, 0));
        DB_MAP.put(2, new Data(2, 2, 0));
        DB_MAP.put(3, new Data(3, 3, 0));
        DB_MAP.put(4, new Data(4, 4, 0));
        DB_MAP.put(5, new Data(5, 5, 0));
    }

    /**
     * 更新方法
     */
    public void update(Data data) {
        Data dbData = DB_MAP.get(data.id);
        if (dbData == null) {
            return;
        }

        System.out.println("update in, dbData : " + dbData + ", data : " + data);

        // 调用具体的同步方法, 类如MYSQL的行锁, 只锁住需要更新的数据行, 不影响其它行的操作
        dbData.update(data);
    }

    /**
     * 查询数据
     *
     * @param id
     * @return
     */
    public Data get(int id) {
        return DB_MAP.get(id);
    }

    public static void main(String[] args) {
        OptimisticLock lockTest = new OptimisticLock();

        // 这里只去更新1、2行的数据, 模拟高并发
        for (int i = 0; i < 80; i++) {
            final int id = (i % 2 == 0) ? 2 : 1;
            final int value = i;
            new Thread(() -> {
                Data data = lockTest.get(id);
                Data newData = new Data(data.id, value, data.version);
                lockTest.update(newData);
            }).start();
        }
    }

    /**
     * 数据行模拟类
     */
    private static class Data {

        int id;

        int value;

        int version;

        Data(int id, int value, int version) {
            this.id = id;
            this.value = value;
            this.version = version;
        }

        /**
         * 具体的同步方法, 这里针对每个具体类锁住, 类如MYSQL的行锁, 只锁住需要更新的数据行, 不影响其它行的操作
         *
         * @param data
         */
        public synchronized void update(Data data) {

            if (data.version != this.version) {
                System.out.println("version is not eq, dbData : " + this + ", data : " + data + "×××××××××");
            } else {
                this.value = data.value;
                this.version++;

                DB_MAP.put(this.id, this);

                System.out.println("Update success, dbData : " + this + ", data : " + data + " " + "√√√√√√√√√");
            }
        }

        @Override
        public String toString() {
            return "{" +
                    "id=" + id +
                    ", value=" + value +
                    ", version=" + version +
                    '}';
        }
    }
}
